'''
tickets1, webapp to insert barcode to SVG templates from database.
Copyright (C) 2018 Rodrigo Garcia

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
'''

from django.db import models
from django.urls import reverse
from django.utils.timezone import now
import uuid
import os

from django.core.exceptions import ObjectDoesNotExist

from .utils import STATIC_SVGS, STATIC_GENERATED_SVGS, STATIC_SVG_TEMPLATES_FOLDER,\
     eliminarArchivo, \
     generarCodioDeBarras, introducirBarcodeSVGEnOtroSVG, generarPdfDesdeSVG,\
     introducirQrCodeSvgEnOtroSvg, dicEtiquetasParaSVG, generarCodigoQr, \
     reemplazarEtiquetasEnSVG, UnicoPdf

# Create your models here.

DEPARTAMENTOS_CHOICES = [
    ('LP', 'La Paz'),
    ('CBBA', 'Cochabamba'),
    ('SC', 'Santa Cruz'),
    ('OR', 'Oruro'),
    ('BE', 'Beni'),
    ('PA', 'Pando'),
    ('CH', 'Chuquisaca'),
    ('TA','Tarija'),
    ('PO','Potosí'),
]

#DEFAULT_SVG_TEMPLATE = 'stickert_mod_m.svg'
DEFAULT_SVG_TEMPLATE = 'ticket1m.svg'

#### Tienda
class Tienda(models.Model):
    nombre = models.CharField(max_length=550,
                              default="-")
    departamento = models.CharField(max_length=4,
                                  choices=DEPARTAMENTOS_CHOICES,
                                  default='LP')
    
    direccion = models.CharField(max_length=500,
                                 default='-')

    fecha_registro = models.DateTimeField(auto_now_add=True)
    
    factura_nro_inicio = models.IntegerField(default=0,
                                             help_text='llene con cuidado (no podra editar despues)')
    factura_nro_final = models.IntegerField(default=1000,
                                            help_text='llene con cuidado (no podra editar despues)')

    template_svg = models.CharField(max_length=500,
                                    default=DEFAULT_SVG_TEMPLATE,
                                        help_text="Si no se proporciona se usa valor por defecto 'ticket1m.svg' (puede dejarlo asi)")

    nit = models.CharField(max_length=255,
                           default='-',
                           help_text="opcional")

    def __str__(self):
        r = str(self.id)+':'+self.nombre+' ('+self.departamento +')'
        return r
    
    def get_absolute_url(self):
        return reverse('tienda-detalles', kwargs={'pk': self.pk})

#### Precio
class Precio(models.Model):
    costo_numeral = models.DecimalField(max_digits=5,
                                        decimal_places=2,
                                        default=1.0,
                                        help_text="Precio numeral (ej: 15.50, 1000.00)")
    costo_literal = models.CharField(max_length=350,
                                     default=" ",
                                     help_text="Precio en literal (ej: MIL, CINCUENTA Y CINCO)")

    def __str__(self):
        r = str(self.id)+': '+str(self.costo_numeral)+' = '+self.costo_literal
        return r

    def get_absolute_url(self):
        return reverse('precio-detalles', kwargs={'pk': self.pk})
    
### GrupoTickets    
class GrupoTickets(models.Model):
    tienda = models.ForeignKey('Tienda',
                               on_delete=models.CASCADE,
                                   default=1)
    precio = models.ForeignKey('Precio',
                               on_delete=models.CASCADE,
                               default=1)
    
    inicio_nro_factura = models.IntegerField(default=1,
                                             help_text="Número de inicio de facturación (no puede cambiarse una vez creado)")

    nro_tickets = models.IntegerField(default=100,
                                      help_text="Número de tickets a generar (no puede cambiarse una vez creado)")
    cod_control_inicio = models.CharField(max_length=255,
                                          default='-',
                                         help_text="Codigo de control inicial, utilice '-' para separar la parte numerica (ej. 'VE-100' ,'Concierto X-0','entrada general-2000' ")
    
    template_svg = models.CharField(max_length=500,
                                    default='-',
                                    help_text="(puede dejar asi) se usa por defecto el template SVG de la tienda o comercio al que pertenece este ticket")
    desc_producto = models.CharField(max_length=500,
                                     default='-',
                                     help_text="desc. o tipo de producto")
    
    fecha_creacion = models.DateTimeField(auto_now_add=True,
                                          editable=False)

    def __str__(self):
        return 'id:'+str(self.id)+' cantidad:'+str(self.nro_tickets)+' - tienda:'+str(self.tienda)

    def save(self, *args, **kwargs):
        if self.template_svg == '-' or len(self.template_svg) < 4:
            # heredar template de tienda
            self.template_svg = self.tienda.template_svg
        if not nroTicketsEstaEnRango(self.tienda, self.nro_tickets):
            # error
            print ('No se puede facturar tantos tickets, solo se pueden hasta '+
                    str(self.tienda.factura_nro_final - ultimoNumeroFactura(self.tienda))+
                    ' adicionales')
            return -1
        # asignando nro de inicio de factura automaticamente
        self.inicio_nro_factura = ultimoNumeroFactura(self.tienda)
        super().save(*args, **kwargs) # call the "real" save() method

    def get_absolute_url(self):
        return reverse('gtickets-detalles', kwargs={'pk': self.pk})

    def CrearTickets(self):
        ''' Este metodo crea los tickets necesarios
        return -- Ids de los tickets creados'''
        return crearTickets(self.id)

    def GenerarPdf(self, limpiar=True):
        '''Genera un unico archivo pdf de los tickets generados usando
        crearPdfGrupoTickets().
        limpiar -- Si es True elimina tambien pdfs y svgs adicionales creados (default: True)
        return -- el nombre del archivo pdf generado'''
        # comprobando si ya se han creado los objetos tickets necesarios
        n = 0
        try:
            n = Ticket.objects.filter(grupo_tickets=self.id).count()
        except ObjectDoesNotExist as e:
            print (e)
        if n < self.nro_tickets:
            self.CrearTickets()

        return crearPdfGrupoTickets(self.id, limpiar=limpiar)

#### Ticket
class Ticket(models.Model):
    grupo_tickets = models.ForeignKey('GrupoTickets',
                                      on_delete=models.CASCADE,
                                          default=1)
    nro_factura = models.IntegerField(default=0)
    codigo_de_barras = models.CharField(max_length=13,
                                        default='0000000000000',
                                            editable=False)

    # este campo debe ir incrementadose automaticamente
    # segun el codigo de control del Grupo de tickets 'padre'
    cod_control = models.CharField(max_length=255,
                                   default='-')
    
    verificado = models.BooleanField(default=False)
    fecha_verificacion = models.DateTimeField(blank=True, null=True)

    #### metodos ####
    def __str__(self):
        return 'barcode: '+self.codigo_de_barras+', cod. control: ' + self.cod_control + ' verif. :'+str(self.verificado)

    def save(self, *args, **kwargs):
        # codigo de barras al azar y "unico"
        if self.codigo_de_barras == '0000000000000':
            self.codigo_de_barras = str(int(uuid.uuid4()))[0:13]
        super().save(*args, **kwargs)

    def GenerarSvg(self):
        barcode_svg = generarCodioDeBarras(self.codigo_de_barras, STATIC_GENERATED_SVGS)
        cadena_qr = '{ "ctrl":"'+str(self.cod_control) + '","fac":'+str(self.nro_factura)+'}'
        qrcode_svg = generarCodigoQr(cadena_qr, STATIC_GENERATED_SVGS,
                                     filename=self.cod_control+'.svg')
        # introduciendo el codigo de barras
        svg_generado = introducirBarcodeSVGEnOtroSVG(barcode_svg,
                                                     STATIC_SVG_TEMPLATES_FOLDER +
                                                     self.grupo_tickets.template_svg)
        print('svg_generado:',svg_generado)
        # introduciendo el codigo qr
        svg_generado = introducirQrCodeSvgEnOtroSvg(qrcode_svg, svg_generado)
        print('//svg_generado:',svg_generado)
        #TODO: mover  esta seccion ####
        eliminarArchivo(barcode_svg)
        # ####
        # preparando datos para reemplazar en la plantilla SVG
        dict = dicEtiquetasParaSVG(self)
        reemplazarEtiquetasEnSVG(dict, svg_generado)
        return svg_generado
    
    def GenerarPdf(self):
        generado = self.GenerarSvg()
        return generarPdfDesdeSVG(generado)


#### utils database Models

def ultimoNumeroFactura(tienda):
    ''' Dada una tienda, busca entre todos los objetos GrupoTickets 
    por los numeros de factura creados y devuelve el ultimo numero
    hasta donde se ha usado 

    tienda -- Objeto Tienda (obligatorio)'''
    if tienda is None:
        return None

    try:
        gt = GrupoTickets.objects.all().filter(tienda=tienda.id).\
          order_by('inicio_nro_factura').last()
        if gt is None:
            # probablemente no hay otros GrupoTickets para esa tienda
            return tienda.factura_nro_inicio
        ultimo = gt.inicio_nro_factura + gt.nro_tickets
        return ultimo

    except ObjectDoesNotExist as e:
        print (e)
        return None

def ultimoNumeroControl(tienda):
    ''' En una `tienda' dada busca todos los objetos GrupoTickets hijos y
    encuentra el ultimo `codigo_de_control' asignado.

    tienda -- Objeto Tienda (obligatorio)
    return -- El ultimo nro de control utilizado (string)
    '''
    if tienda is None:
        return None
    try:
        gt = GrupoTickets.objects.all().filter(tienda=tienda.id).\
          order_by('id').last()
        if gt is None:
            # probablemente no hay otros GrupoTickets para esta tienda
            return tienda.cod_control_inicio
        # extrayendo la parte numerica del codigo
        try:
            return int(gt.cod_control_inicio.split("-")[-1])
        except ValueError as e:
            print (e)
            return 0
    except ObjectDoesNotExist as e:
        print (e)
        
def nroTicketsEstaEnRango(tienda, nro_tickets):
    ''' Verifica si en la `tienda' dada se puede facturar el
    `numero de tickets' dado.

    tienda -- Objeto Tienda (obligatorio)
    nro_tickets -- entero numero de tickets (obligatorio)
    '''
    if tienda is None or nro_tickets is None:
        return None
    ultimo_numero_factura = ultimoNumeroFactura(tienda)
    return (ultimo_numero_factura + nro_tickets <= tienda.factura_nro_final)

def crearTickets(id):
    ''' A partir del `id' crea los objetos Tickets y los guarda en la 
    base de datos.

    id -- id del objeto `GrupoTickets' (obligatorio)
    return -- Los ids de los tickets creados
    '''
    ids_creados = []
    try:
        gt = GrupoTickets.objects.get(id=id)
        ultimo_numero_control = ultimoNumeroControl(gt.tienda)
        for i in range(0, gt.nro_tickets):
            ticket = Ticket(grupo_tickets=gt,
                            nro_factura=gt.inicio_nro_factura+i)
            num_control = ultimo_numero_control + i
            # uniendo y registrando el codigo de control
            ticket.cod_control = gt.cod_control_inicio.split('-')[0] + ' ' + str(num_control)
            ticket.save()
            ids_creados.append(ticket.id)
    except ObjectDoesNotExist as e:
        print (e)
        return 0
    return ids_creados

def crearPdfGrupoTickets(id, limpiar=True):
    ''' A partir del `id' crea un archivo PDF que contiene todos los 
    tickets generados a partir de la plantilla svg que tiene cada ticket.
    Esta funcion genera 1 archivo svg por cada ticket, lo exporta en
    pdf y une esos pdfs en uno solo usando `pdftk'. 
    
    id -- id del objeto GrupoTickets (obligatorio)
    limpiar -- Si es True se borran los archivos svg y individuales
    generados por cada ticket (default: True)
    return -- Nombre del archivo pdf creado
    '''
    if id is None:
        return None
    try:
        gt = GrupoTickets.objects.get(id=id)
        tickets = Ticket.objects.filter(grupo_tickets=id).all()
        pdfs = []
        for ticket in tickets:
            pdf = ticket.GenerarPdf()
            #print ("pdf generado:"+pdf)
            pdfs.append(pdf)
        unico_pdf = UnicoPdf(pdfs, nombre=gt.cod_control_inicio)
        print ("unico pdf generado:"+str(unico_pdf))
        if limpiar:
            for pdf in pdfs:
                # eliminando el svg (si existe)
                eliminarArchivo(pdf.split('.pdf')[0])
    except ObjectDoesNotExist as e:
        print (e)
        return None
    return unico_pdf
# temporales
def mostrarTiendas():
    print ('Tiendas:')
    for tienda in Tienda.objects.all():
        print(tienda)

def mostrarGrupoTickets():
    print ('Grupo Tickets:')
    for gt in GrupoTickets.objects.all():
        print(gt)
